package main

import (
	"bytes"
	"fmt"
	"illuminant/config"
	"illuminant/controller"
	"illuminant/middleware"
	"illuminant/util"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"
	"path"
	"strconv"

	"gitee.com/wangyubin/gutils"
	"github.com/gin-contrib/cors"
	"github.com/gin-contrib/static"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
)

var upGrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func route(port int) error {

	cnf := config.GetConfig()
	r := gin.Default()
	if gin.Mode() == "debug" {
		r.Use(cors.Default())
	}

	authMiddleware := middleware.JwtMiddleware()
	apiV1 := r.Group("/api/v1")

	// no auth routes
	apiV1.POST("/login", authMiddleware.LoginHandler)

	// auth routes
	authRoute := apiV1.Group("/")
	authRoute.GET("/refresh_token", authMiddleware.RefreshHandler)
	authRoute.Use(authMiddleware.MiddlewareFunc())
	{
		// 测试用
		authRoute.GET("/test", func(c *gin.Context) {
			util.Success(c, "success", nil)
		})
		authRoute.GET("/current_user", func(c *gin.Context) {
			var userInfo = map[string]string{
				"name":   "user",
				"avatar": "",
			}
			util.Success(c, "success", userInfo)
		})

		// todo table CURD with ORM(gorose)
		authRoute.POST("/createTodo", controller.CreateTodo)
		authRoute.POST("/getTodos", controller.GetTodos)
		authRoute.POST("/deleteTodo", controller.DeleteTodo)

		// proxy prisma graphql
		authRoute.POST("/graphql", ReverseProxy())

		// file upload
		authRoute.POST("/upload", func(c *gin.Context) {
			file, header, err := c.Request.FormFile("file")
			if err != nil {
				util.Fail(c, util.DOWNLOAD_ERROR, "param error: "+err.Error(), nil)
				return
			}

			filename := header.Filename
			fmt.Printf("filename: %s\n", filename)
			// 这里为了演示上传文件的API, 只是简单把文件放入 upload 文件夹,
			// 实际情况可能还有解析文件, 写入文件信息到数据库等
			cnf := config.GetConfig()
			out, _ := os.Create(path.Join(cnf.File.UploadDir, filename))
			defer out.Close()
			_, err = io.Copy(out, file)
			if err != nil {
				util.Fail(c, util.UPLOAD_ERROR, err.Error(), nil)
				return
			}

			util.Success(c, "upload success", nil)
		})

		// file download
		authRoute.GET("/download/:fileId", func(c *gin.Context) {
			fileId := c.Param("fileId")
			// 创建个文件供下载
			cnf := config.GetConfig()
			fp := path.Join(cnf.File.DownloadDir, fileId)
			f, _ := os.Create(fp)
			_, err := f.WriteString("hello illuminant")
			if err != nil {
				util.Fail(c, util.DOWNLOAD_ERROR, err.Error(), nil)
				return
			}
			f.Close()

			c.File(fp)
		})

		// websocket
		authRoute.GET("/ws", func(c *gin.Context) {
			ws, err := upGrader.Upgrade(c.Writer, c.Request, nil)
			if err != nil {
				log.Println("error get connection")
				log.Fatal(err)
			}
			defer ws.Close()
			//读取ws中的数据
			mt, message, err := ws.ReadMessage()
			if err != nil {
				log.Println("error read message")
				log.Fatal(err)
			}

			// 返回message
			message = []byte(string(message) + " => response")
			err = ws.WriteMessage(mt, message)
			if err != nil {
				log.Println("error write message: " + err.Error())
			}
		})
	}

	// static files
	// 这个route用来发布前端用, 前端编译之后, 放入public 文件夹即可
	r.Use(static.Serve("/", static.LocalFile(cnf.File.StaticDir, true)))

	r.NoRoute(func(c *gin.Context) {
		c.File("./public/index.html")
	})

	return r.Run(":" + strconv.Itoa(port))
}

func ReverseProxy() gin.HandlerFunc {

	conf := config.GetConfig()
	u, err := url.Parse(conf.Prisma.Endpoint)
	gutils.CheckError(err)

	return func(c *gin.Context) {
		director := func(req *http.Request) {
			req.URL.Scheme = u.Scheme
			req.URL.Host = u.Host
			req.URL.Path = u.Path
			delete(req.Header, "Authorization")
			req.Header["Authorization"] = []string{"Bearer " + conf.Prisma.JwtToken}

		}

		body, err := c.GetRawData()
		if err != nil {
			fmt.Println(err)
		}

		// TODO 对body进行权限check

		fmt.Printf("%s\n", string(body))
		c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body))

		proxy := &httputil.ReverseProxy{Director: director}
		proxy.ModifyResponse = util.RewriteBody
		proxy.ServeHTTP(c.Writer, c.Request)
	}
}
